#include<stdio.h>
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>

#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " "
#define ARGC_SIZE 32
#define EXIT_CODE 45
#define LINE_SIZE 1024

int lastcode = 0;
extern char **environ;
char commandline[LINE_SIZE];
char* argv[ARGC_SIZE];
int quit = 0;
char pwd[LINE_SIZE];

// 自定义环境变量表
char myenv[LINE_SIZE];
// 自定义本地变量表


#define FARMAT LEFT"%s@%s %s"RIGHT""LABLE" "

const char* getusername()
{
    return getenv("USER");
}

const char* getpostname()
{
    return getenv("HOSTNAME");
}

const char* getpwd()
{
    getcwd(pwd,sizeof(pwd)-1);
}

void Interact(char *cline, int size)
{
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE"",getusername(),getpostname(),pwd);
    char *s = fgets(cline,size-1,stdin);  //获取文件字符串
    assert(s!=NULL);
    (void)s; // 抵消编译器的告警

    // 将\n -> \0
    cline[strlen(cline)-1] = '\0';
}

int Splitstring(char *cline, char* argv[])
{
    int i = 0;
    argv[i++] = strtok(cline,DELIM); // 第一次截用参数 后面用null
    while(argv[i++] = strtok(NULL,DELIM)); // 并将最后一个置为NULL
    return i-1;
}

void NormalExcute(char* argv[])
{
    pid_t id = fork();
    if(id<0)
    {
        perror("fork");
        return;
    }
    else if(id==0)
    {
        // 让子进程执行命令
        execvpe(argv[0],argv,environ);
        exit(EXIT_CODE);
    }
    else
    {
        int status = 0;
        pid_t rid =  waitpid(id,&status,0);
        if(rid == id)
        {
            lastcode = WEXITSTATUS(status);
        }
    }
}

int buildCommand(char* argv[], int argc)
{
    if(argc == 2 && strcmp(argv[0],"cd") == 0)
    {
        chdir(argv[1]);
        getpwd();
        sprintf(getenv("PWD"),"%s",pwd);
        return 1;
    }
    else if(argc == 2 && strcmp(argv[0],"export") == 0)
    {
        // putenv(argv[1]); // 修改环境变量的指针，但argv这个指针一直在变
        // 因此，创建一个缓冲区来保存他
        strcpy(myenv,argv[1]);
        putenv(myenv); 
        return 1;
    }
    else if(argc == 2 && strcmp(argv[0],"echo") == 0)
    {
        if(strcmp(argv[1],"$?") == 0) // 查看退出码
        {
            printf("%d\n",lastcode);
            lastcode = 0;
        }
        else if(*argv[1] == '$')
        {
            char* val = getenv(argv[1]+1);
            if(val)
                printf("%s\n",val);
        }
        else
        {
            printf("%s\n",argv[1]);
        }
        return 1;
    }

    // 特殊处理一下
    if(strcmp(argv[0],"ls") == 0)
    {
        argv[argc++] = "--color";
        argv[argc] = NULL;
    }
    return 0;
}

int main()
{
    while(!quit)
    {
        // 2.交互问题，获取命令行。
        Interact(commandline,sizeof(commandline));
        // printf("%s",commandline);

        // 3.字串分割问题，解析命令行
        int argc = Splitstring(commandline,argv);
        if(argc == 0)   continue;

        // 4.指令的判断 判断是不是内建命令,内建命令，本身就是一个shell内部的一个函数
        // for(int i=0;i<argc;i++)
        // {
        //     printf("%s\n",argv[i]);
        // }
        int n = buildCommand(argv,argc);


        // 5.普通命令的执行
        if(n==0)
            NormalExcute(argv);
    }
    return 0;
}